gdk/frameclock: Make surfaces inhibit freeze
authorJonas Ådahl <jadahl@gmail.com>
Fri, 28 Jun 2019 16:45:44 +0000 (18:45 +0200)
committerJonas Ådahl <jadahl@gmail.com>
Wed, 3 Jul 2019 09:42:28 +0000 (11:42 +0200)
To make a frame clock tick as long as any of the associated surfaces
expect to receive ticks, make the surfaces inhibit freezing the clock,
instead of directly tell the frame clock to freeze itself.

This makes it so that as long as any surface using a certain frame clock
is not frozen (e.g. just received a frame event from the display
server), the frame clock will not be frozen.

With this, the frame clock is initiated as frozen, and won't be thawed
until any surface inhibits freeze. It will be frozen again, when every
surface has that previously inhibited freeze uninhibited freeze.

gdk/broadway/gdksurface-broadway.c
gdk/gdkframeclock.c
gdk/gdkframeclockprivate.h
gdk/gdksurface.c
gdk/gdksurfaceprivate.h
gdk/wayland/gdksurface-wayland.c
gdk/x11/gdkdisplay-x11.c
gdk/x11/gdksurface-x11.c

index 88f1b9de85b53673c67f944a5c5f416515eeed18..6d072359ac21ef4bef36ce2e25bcc09353635486 100644 (file)
@@ -88,10 +88,10 @@ gdk_broadway_surface_finalize (GObject *object)
 }
 
 static gboolean
-thaw_clock_cb (GdkFrameClock *clock)
+thaw_updates_cb (GdkSurface *surface)
 {
-  _gdk_frame_clock_thaw (clock);
-  g_object_unref (clock);
+  gdk_surface_thaw_updates (surface);
+  g_object_unref (surface);
   return G_SOURCE_REMOVE;
 }
 
@@ -109,9 +109,9 @@ _gdk_broadway_roundtrip_notify (GdkSurface  *surface,
 
   /* If there is no remote web client, rate limit update to once a second */
   if (local_reply)
-    g_timeout_add_seconds (1, (GSourceFunc)thaw_clock_cb, g_object_ref (clock));
+    g_timeout_add_seconds (1, (GSourceFunc)thaw_updates_cb, g_object_ref (surface));
   else
-    _gdk_frame_clock_thaw (clock);
+    gdk_surface_thaw_updates (surface);
 
   if (timings)
     {
@@ -140,7 +140,7 @@ on_frame_clock_after_paint (GdkFrameClock *clock,
   GdkBroadwayDisplay *broadway_display;
 
   impl->pending_frame_counter = gdk_frame_clock_get_frame_counter (clock);
-  _gdk_frame_clock_freeze (gdk_surface_get_frame_clock (surface));
+  gdk_surface_freeze_updates (surface);
 
   broadway_display = GDK_BROADWAY_DISPLAY (display);
 
index 6d79b1810e6f24d1ed90597ffcc00ba33e1cd231..cf3ae0b16d24cd300970be3b08818599856af105 100644 (file)
@@ -97,10 +97,14 @@ struct _GdkFrameClockPrivate
   gint n_timings;
   gint current;
   GdkFrameTimings *timings[FRAME_HISTORY_MAX_LENGTH];
+  gint n_freeze_inhibitors;
 };
 
 G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (GdkFrameClock, gdk_frame_clock, G_TYPE_OBJECT)
 
+static void
+_gdk_frame_clock_freeze (GdkFrameClock *clock);
+
 static void
 gdk_frame_clock_finalize (GObject *object)
 {
@@ -114,12 +118,21 @@ gdk_frame_clock_finalize (GObject *object)
   G_OBJECT_CLASS (gdk_frame_clock_parent_class)->finalize (object);
 }
 
+static void
+gdk_frame_clock_constructed (GObject *object)
+{
+  G_OBJECT_CLASS (gdk_frame_clock_parent_class)->constructed (object);
+
+  _gdk_frame_clock_freeze (GDK_FRAME_CLOCK (object));
+}
+
 static void
 gdk_frame_clock_class_init (GdkFrameClockClass *klass)
 {
   GObjectClass *gobject_class = (GObjectClass*) klass;
 
   gobject_class->finalize     = gdk_frame_clock_finalize;
+  gobject_class->constructed  = gdk_frame_clock_constructed;
 
   /**
    * GdkFrameClock::flush-events:
@@ -335,7 +348,7 @@ gdk_frame_clock_end_updating (GdkFrameClock *frame_clock)
   GDK_FRAME_CLOCK_GET_CLASS (frame_clock)->end_updating (frame_clock);
 }
 
-void
+static void
 _gdk_frame_clock_freeze (GdkFrameClock *clock)
 {
   g_return_if_fail (GDK_IS_FRAME_CLOCK (clock));
@@ -344,7 +357,7 @@ _gdk_frame_clock_freeze (GdkFrameClock *clock)
 }
 
 
-void
+static void
 _gdk_frame_clock_thaw (GdkFrameClock *clock)
 {
   g_return_if_fail (GDK_IS_FRAME_CLOCK (clock));
@@ -352,6 +365,35 @@ _gdk_frame_clock_thaw (GdkFrameClock *clock)
   GDK_FRAME_CLOCK_GET_CLASS (clock)->thaw (clock);
 }
 
+void
+_gdk_frame_clock_inhibit_freeze (GdkFrameClock *clock)
+{
+  GdkFrameClockPrivate *priv;
+
+  g_return_if_fail (GDK_IS_FRAME_CLOCK (clock));
+
+  priv = clock->priv;
+
+  priv->n_freeze_inhibitors++;
+  if (priv->n_freeze_inhibitors == 1)
+    _gdk_frame_clock_thaw (clock);
+}
+
+
+void
+_gdk_frame_clock_uninhibit_freeze (GdkFrameClock *clock)
+{
+  GdkFrameClockPrivate *priv;
+
+  g_return_if_fail (GDK_IS_FRAME_CLOCK (clock));
+
+  priv = clock->priv;
+
+  priv->n_freeze_inhibitors--;
+  if (priv->n_freeze_inhibitors == 0)
+    _gdk_frame_clock_freeze (clock);
+}
+
 /**
  * gdk_frame_clock_get_frame_counter:
  * @frame_clock: a #GdkFrameClock
index 17d063b4344b143c7537981d6e2e62ff37c10e40..7ddceedb8082514cf1c9e2accf38fa0ec3ae97d3 100644 (file)
@@ -105,8 +105,8 @@ struct _GdkFrameTimings
   guint slept_before : 1;
 };
 
-void _gdk_frame_clock_freeze (GdkFrameClock *clock);
-void _gdk_frame_clock_thaw   (GdkFrameClock *clock);
+void _gdk_frame_clock_inhibit_freeze (GdkFrameClock *clock);
+void _gdk_frame_clock_uninhibit_freeze (GdkFrameClock *clock);
 
 void _gdk_frame_clock_begin_frame         (GdkFrameClock   *clock);
 void _gdk_frame_clock_debug_print_timings (GdkFrameClock   *clock,
index 44c66a66cd25cc0002a9f439bb44ee35ef334457..70c2c409a00e0eb2582adf935d363a60cce498a9 100644 (file)
@@ -1341,10 +1341,14 @@ gdk_surface_schedule_update (GdkSurface *surface)
 {
   GdkFrameClock *frame_clock;
 
-  if (surface &&
-      (surface->update_freeze_count ||
-       gdk_surface_is_toplevel_frozen (surface)))
-    return;
+  g_return_if_fail (surface);
+
+  if (surface->update_freeze_count ||
+      gdk_surface_is_toplevel_frozen (surface))
+    {
+      surface->pending_schedule_update = TRUE;
+      return;
+    }
 
   /* If there's no frame clock (a foreign surface), then the invalid
    * region will just stick around unless gdk_surface_process_updates()
@@ -1581,13 +1585,17 @@ gdk_surface_freeze_updates (GdkSurface *surface)
   g_return_if_fail (GDK_IS_SURFACE (surface));
 
   surface->update_freeze_count++;
+  if (surface->update_freeze_count == 1)
+    _gdk_frame_clock_uninhibit_freeze (surface->frame_clock);
 }
 
 /**
  * gdk_surface_thaw_updates:
  * @surface: a #GdkSurface
  *
- * Thaws a surface frozen with gdk_surface_freeze_updates().
+ * Thaws a surface frozen with gdk_surface_freeze_updates(). Note that this
+ * will not necessarily schedule updates if the surface freeze count reaches
+ * zero.
  **/
 void
 gdk_surface_thaw_updates (GdkSurface *surface)
@@ -1597,7 +1605,15 @@ gdk_surface_thaw_updates (GdkSurface *surface)
   g_return_if_fail (surface->update_freeze_count > 0);
 
   if (--surface->update_freeze_count == 0)
-    gdk_surface_schedule_update (surface);
+    {
+      _gdk_frame_clock_inhibit_freeze (surface->frame_clock);
+
+      if (surface->pending_schedule_update)
+        {
+          surface->pending_schedule_update = FALSE;
+          gdk_surface_schedule_update (surface);
+        }
+    }
 }
 
 void
@@ -1606,7 +1622,7 @@ gdk_surface_freeze_toplevel_updates (GdkSurface *surface)
   g_return_if_fail (GDK_IS_SURFACE (surface));
 
   surface->update_and_descendants_freeze_count++;
-  _gdk_frame_clock_freeze (gdk_surface_get_frame_clock (surface));
+  gdk_surface_freeze_updates (surface);
 }
 
 void
@@ -1616,9 +1632,9 @@ gdk_surface_thaw_toplevel_updates (GdkSurface *surface)
   g_return_if_fail (surface->update_and_descendants_freeze_count > 0);
 
   surface->update_and_descendants_freeze_count--;
-  _gdk_frame_clock_thaw (gdk_surface_get_frame_clock (surface));
-
   gdk_surface_schedule_update (surface);
+  gdk_surface_thaw_updates (surface);
+
 }
 
 /**
@@ -3718,6 +3734,9 @@ gdk_surface_set_frame_clock (GdkSurface     *surface,
                             G_CALLBACK (gdk_surface_paint_on_clock),
                             surface);
         }
+
+      if (surface->update_freeze_count == 0)
+        _gdk_frame_clock_inhibit_freeze (clock);
     }
 
   if (surface->frame_clock)
@@ -3737,6 +3756,10 @@ gdk_surface_set_frame_clock (GdkSurface     *surface,
                                                 G_CALLBACK (gdk_surface_paint_on_clock),
                                                 surface);
         }
+
+      if (surface->update_freeze_count == 0)
+        _gdk_frame_clock_uninhibit_freeze (surface->frame_clock);
+
       g_object_unref (surface->frame_clock);
     }
 
index ce81b23a8441ab0093cd7abc19c2516979ee8571..5f86e3c60a89e06078201cd5c742248e080f6bba 100644 (file)
@@ -49,6 +49,7 @@ struct _GdkSurface
 
   cairo_region_t *update_area;
   guint update_freeze_count;
+  gboolean pending_schedule_update;
   /* This is the update_area that was in effect when the current expose
      started. It may be smaller than the expose area if we'e painting
      more than we have to, but it represents the "true" damage. */
index 1bc06568c7e1f3da209133e684c3c85e1bb3293c..af42ff267f398c18338de2e8dc68b9ad0bdb1856 100644 (file)
@@ -349,7 +349,7 @@ frame_callback (void               *data,
     return;
 
   impl->awaiting_frame = FALSE;
-  _gdk_frame_clock_thaw (clock);
+  gdk_surface_thaw_updates (surface);
 
   timings = gdk_frame_clock_get_timings (clock, impl->pending_frame_counter);
   impl->pending_frame_counter = 0;
@@ -465,8 +465,9 @@ on_frame_clock_after_paint (GdkFrameClock *clock,
       g_signal_emit (impl, signals[COMMITTED], 0);
     }
 
-  if (impl->awaiting_frame)
-    _gdk_frame_clock_freeze (clock);
+  if (impl->awaiting_frame &&
+      impl->pending_frame_counter == gdk_frame_clock_get_frame_counter (clock))
+    gdk_surface_freeze_updates (surface);
 }
 
 void
@@ -2577,12 +2578,8 @@ gdk_wayland_surface_hide_surface (GdkSurface *surface)
 
       if (impl->awaiting_frame)
         {
-          GdkFrameClock *frame_clock;
-
           impl->awaiting_frame = FALSE;
-          frame_clock = gdk_surface_get_frame_clock (surface);
-          if (frame_clock)
-            _gdk_frame_clock_thaw (frame_clock);
+          gdk_surface_thaw_updates (surface);
         }
 
       if (impl->display_server.gtk_surface)
index 60a8892c59db1da85200d0cdba3f5a2070b2adb9..58ca9f1aa5805ae26df57f1d403481828897f175 100644 (file)
@@ -874,7 +874,7 @@ gdk_x11_display_translate_event (GdkEventTranslator *translator,
               surface_impl->toplevel->frame_pending)
             {
               surface_impl->toplevel->frame_pending = FALSE;
-              _gdk_frame_clock_thaw (gdk_surface_get_frame_clock (event->any.surface));
+              gdk_surface_thaw_updates (event->any.surface);
             }
 
          if (toplevel)
@@ -1238,7 +1238,7 @@ _gdk_wm_protocols_filter (const XEvent *xevent,
           if (surface_impl->toplevel->frame_pending)
             {
               surface_impl->toplevel->frame_pending = FALSE;
-              _gdk_frame_clock_thaw (clock);
+              gdk_surface_thaw_updates (event->any.surface);
             }
 
           gdk_frame_clock_get_refresh_info (clock,
index f4ecd5d2f8ea22204af37c7fc3a43873c38a2c32..654876e2b89484384b220996a9cf2b8a3ea4cdc9 100644 (file)
@@ -397,7 +397,7 @@ gdk_x11_surface_end_frame (GdkSurface *surface)
                                               g_intern_static_string ("_NET_WM_FRAME_DRAWN")))
         {
           impl->toplevel->frame_pending = TRUE;
-          _gdk_frame_clock_freeze (gdk_surface_get_frame_clock (surface));
+          gdk_surface_freeze_updates (surface);
           timings->cookie = impl->toplevel->current_counter_value;
         }
     }